/**
 * @author Kristian Mandrup
 * @date June 10, 2009
 * @version 0.1
 * 
 * m3gen :: ChildrenParser
 * 
 * - Finds children for the jsonObj and creates a tree of nodeObj to be used later for constructing a tree of jsTreeNodes
 * 
 * Usage 
 * -----
 * var my_ChildrenParser = new ChildrenParser(ChildrenParserOptions);
 * var nodeObjChildren = my_ChildrenParser.parse(jsonObj);
 * 
 * Inherits from Parser
 * 
 * - getObjTypeFromObj
 * 
 * - getRuleSetByObjType
 * 
 * - getActiveRuleSet
 * - setActiveRuleSet
 * 
 * - getActiveObj
 * - setActiveObj
 * 
 * - getRuleSetConfiguration
 * - setRuleSetConfiguration
 * 
 */

/**
 * ChildrenParser constructor
 * Sets up ChildrenParser for use
 * 
 * @param {Object} createOptions
 */
m3gen.ChildrenParser = function(createOptions) {
	if (arguments.length < 1 || m3gen.util.invalidOptions(createOptions))
		return;
	this.init(createOptions);			
}

m3gen.ChildrenParser.inherits(m3gen.Base);

/**
 * Initialize ChildrenParser
 * @param {Object} createOptions
 */
m3gen.ChildrenParser.prototype.init = function(createOptions){
	this.setClassName('ChildrenParser');		
	
	// call superclass initializer
	this.uber('init', createOptions);
}

/**
 * Public
 * A ChildrenParser must always have an execute function
 * 
 * Find instance and reference children and add them to resulting children list
 * 
 */
m3gen.ChildrenParser.prototype.execute = function(){
	// always start with empty list of children
	var nodeChildren = [];
	var children = this.addInstanceChildren(nodeChildren);	
	return children;
}

/**
 * Add Instance children to nodechildren
 * 
 * @param {Object} nodeChildren
 */
m3gen.ChildrenParser.prototype.addInstanceChildren = function(nodeChildren){
	return this.addToNodeChildren(nodeChildren, this.getInstanceBranchRules(), false);
}

/**
 * Add Reference children to nodechildren
 * @param {Object} nodeChildren
 */
m3gen.ChildrenParser.prototype.addReferenceChildren = function(nodeChildren){
	return this.addToNodeChildren(nodeChildren, this.getReferenceBranchRules(), true);
}

/**
 * Generic add children to node children
 * 
 * @param {Object} nodeChildren
 * @param {Object} rules
 * @param {Object} isReference
 */
m3gen.ChildrenParser.prototype.addToNodeChildren = function(nodeChildren, rules, isReference){
	// the branches from the active ruleSet (for child references)						
	var referenceBranchRules = this.getReferenceBranchRules();
	
	if (rules) {
		// parse and resolve children of nodeObj
		var children = this.resolveRuleSetBranches({
			isReference: isReference
		});
		// add children to total list of children for nodeObj
		this.addNodeChildren(nodeChildren, children);
	}
	return nodeChildren;	
}

/**
 * Resolve branches from the ruleSet used to generate reference nodeObj nodes
 * @param {Object} options
 * 
 * Returns
 * An array where each item is of the form: 
 * {branchNode: {...}, children: [{.}, {.}, ...]} or {children:[{.}, {.}, ...]}
 * Fx
 * [{branchNode: {b1}, children: [{A}, {B}, ...]}, {branchNode: {b2}, children: [{D}, {F}, ...]}]
 * Or
 * [{children: [{A}, {B}, ...]}, {branchNode: {b2}, children: [{D}, {F}, ...]}, {children: [{X}, {Y}, ...]}]
 * 
 * Multiple objects containing a children key with no branchNode key must be concatenated into 
 * one children array to be direct children of the originating nodeObj
 *
	options:
	{
		isReference: true/false
	}
 */
m3gen.ChildrenParser.prototype.resolveRuleSetBranches = function(options) {
	// start with empty children array 							
	var allChildren = [];
	var branchRules = this.getBranchRules();
		
	// allChildren is the collection of all chilren in the branches set, 
	// fx all references or branches with references 
	var that = this;		
	branchRules.forEach( function(branchRule) {		
		var branchChildren = that.resolveBranch(branchRule, options);
		that.concatArrays(allChildren, branchChildren);
	});	
	return allChildren; 
}

/**
 * Resolves a single branchRule within a ruleSet
 * @param {Object} branchRule
 * @param {Object} options
 * 
	options:
	{
		isReference: true/false
	}
 * 
 * OLD DOCUMENTAION
 * 
 * Return:
 * In case of a branch definition:
 * {branchNode: {...}, children: [{.}, {.}, ...] where each object in children is a nodeObj
 * 
 * In case of NO branch definition
 * {children:[{.}, {.}, ...]}
 * 
 */
m3gen.ChildrenParser.prototype.resolveBranch = function(branchRule, options){	
	// get the ruleSetBranchDef object from the branchDef in the ruleSet
	var branchDefinition = this.getBranchDefinition(branchRule);
	var childSelectors = this.getChildSelectors(branchRule);	
	var children = [];

	if (this.isNotEmptyArray(childSelectors)) {
		// if there is a branch definition create the branch and parse the children (using selectors)
		var branchNodeObj = null;
		if (this.isValidObj(branchDefinition)) {
			branchNodeObj = this.parseBranch(branchDefinition, childSelectors, options);
		}
		// parse the children directly
		children = this.parseChildren(childSelectors, options);
		
		if (this.isValidObj(branchNodeObj)) {
			if (children.length > 0)
				branchNodeObj.children = children;
			return branchNodeObj;			
		}
	}
	return children;
}

/**
 * Parse all children of a branch using selectors of a branchRule
 * 
 * @param 	{Object} childSelectors
 * @param 	{Object} options	{isReference: true/false}
 * 
 * @return	{Object} children	- ?
 */
m3gen.ChildrenParser.prototype.parseChildren = function(childSelectors, options) {
	
	var checkEmptyBranch = function(elem) { return m3gen.util.isNotEmptyArray(elem.children)}
	
	// start with empty list of children in branch
    var branchChildren = [];
	var nodeType = this.getNodeType();
	var that = this;
	if (this.isNotEmptyArray(childSelectors)) {
		childSelectors.forEach(function(childSelector){
			var children = [];
			var childrenFound = that.findChildrenBySelector(childSelector);
			if (that.isValidObj(childrenFound)) {
				children = that.resolveObjects(childrenFound, options);
				// add children found for this selector to total child list of this branch
				that.concatNodeObjArrays(branchChildren, children, checkEmptyBranch);
			}
			else {
				// console.log('children not found');
			}			
		});
	}
	else {
	}
	// return total child list of this branch
	return branchChildren;

}

/**
 * Parse all children of a branch using selectors of a branchRule

 * @param {Object} branchDefinition
 * @param {Object} childSelectors
 * @param {Object} options 	{isReference: true/false}
 * 	 
 * @return {Branch} branch
 * 

 * 	Same as parseChildren but put children under branch, Use NodeObjBranchBuilder !
 */
m3gen.ChildrenParser.prototype.parseBranch = function(branchDefinition, childSelectors, options) {
	var branchNodeObj = this.getNodeObjBranchBuilderInstance().execute(branchDefinition);
	return branchNodeObj;
}


